package com.jzo2o.market.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.common.utils.*;
import com.jzo2o.market.constants.RedisConstants;
import com.jzo2o.market.constants.TabTypeConstants;
import com.jzo2o.market.enums.ActivityStatusEnum;
import com.jzo2o.market.enums.CouponStatusEnum;
import com.jzo2o.market.mapper.ActivityMapper;
import com.jzo2o.market.model.domain.Activity;
import com.jzo2o.market.model.domain.Coupon;
import com.jzo2o.market.model.dto.request.ActivityQueryForPageReqDTO;
import com.jzo2o.market.model.dto.request.ActivitySaveReqDTO;
import com.jzo2o.market.model.dto.response.ActivityInfoResDTO;
import com.jzo2o.market.service.IActivityService;
import com.jzo2o.market.service.ICouponService;
import com.jzo2o.mysql.utils.PageUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static com.jzo2o.market.enums.ActivityStatusEnum.*;


/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author itcast
 * @since 2023-09-16
 */
@Service
public class ActivityServiceImpl extends ServiceImpl<ActivityMapper, Activity> implements IActivityService {
    private static final int MILLION = 1000000;

    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private ICouponService couponService;
    @Resource
    private ActivityMapper activityMapper;


    @Override
    public void save(ActivitySaveReqDTO activitySaveReqDTO) {
        Activity activity = new Activity();
        activity.setDiscountRate(activitySaveReqDTO.getDiscountRate());
        activity.setDiscountAmount(activitySaveReqDTO.getDiscountAmount());
        activity.setType(activitySaveReqDTO.getType());
        activity.setDistributeEndTime(activitySaveReqDTO.getDistributeEndTime());
        activity.setTotalNum(activitySaveReqDTO.getTotalNum());
        activity.setName(activitySaveReqDTO.getName());
        activity.setAmountCondition(activitySaveReqDTO.getAmountCondition());
        activity.setDistributeStartTime(activitySaveReqDTO.getDistributeStartTime());
        activity.setValidityDays(activitySaveReqDTO.getValidityDays());
        activity.setStatus(1);

        activityMapper.insert(activity);
    }

    @Override
    public PageResult<ActivityInfoResDTO> pageQuery(ActivityQueryForPageReqDTO activityQueryForPageReqDTO) {
        //获取分页对象
        Page<Activity> activityPage = PageUtils.parsePageQuery(activityQueryForPageReqDTO, Activity.class);
        //分页查询，按照发放开始时间排序
        LambdaQueryWrapper<Activity> wrapper = Wrappers.<Activity>lambdaQuery()
                .eq(activityQueryForPageReqDTO.getId() != null,Activity::getId,activityQueryForPageReqDTO.getId())
                .eq(activityQueryForPageReqDTO.getType() != null,Activity::getType,activityQueryForPageReqDTO.getType())
                .eq(activityQueryForPageReqDTO.getStatus() != null,Activity::getStatus,activityQueryForPageReqDTO.getStatus())
                .like(StringUtils.isNotBlank(activityQueryForPageReqDTO.getName()),Activity::getName,activityQueryForPageReqDTO.getName())
                .orderByDesc(Activity::getDistributeStartTime);
        Page<Activity> page = this.page(activityPage, wrapper);
        //返回封装结果
        return PageUtils.toPage(page,ActivityInfoResDTO.class);
    }

    @Override
    public ActivityInfoResDTO getInfoById(Long id) {
        Activity one = lambdaQuery().eq(Activity::getId, id).one();
        return BeanUtil.toBean(one, ActivityInfoResDTO.class);
    }

    /**
     * @param id 活动id
     * @description: 运营端撤销活动
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void revoke(Long id) {
        // 1. 判断id是否存在
        if (ObjectUtils.isNull(id)) {
            throw new BadRequestException("非法活动id");
        }
        // 2. 判断活动是否存在
        Activity activity = this.getById(id);
        if (ObjectUtils.isNull(activity)) {
            throw new BadRequestException("活动不存在");
        }

        // 3. 判断活动是否处于待生效、进行中状态
        List<Integer> activityStatus =
                Arrays.asList(NO_DISTRIBUTE.getStatus(), ActivityStatusEnum.DISTRIBUTING.getStatus());
        if (!activityStatus.contains(activity.getStatus())) {
            throw new BadRequestException("非法操作");
        }

        // 4. 修改活动状态为已作废
        boolean result = this.lambdaUpdate()
                .set(Activity::getStatus, ActivityStatusEnum.VOIDED.getStatus())
                .eq(Activity::getId, id)
                .update();
        if (!result) {
            throw new DBException("撤销活动失败");
        }
        // 5. 作废用户已抢到的优惠券
        List<Coupon> couponList = couponService.lambdaQuery()
                .eq(Coupon::getActivityId, id)
                .list();
        if (CollUtils.isEmpty(couponList)) {
            return;
        }
        couponList.forEach(coupon -> coupon.setStatus(CouponStatusEnum.VOIDED.getStatus()));
        boolean resultForCoupon = couponService.updateBatchById(couponList);
        if (!resultForCoupon) {
            throw new DBException("作废用户已抢到的优惠券失败");
        }
    }


    @Override
    public void updateStatus() {
        LocalDateTime now = DateUtils.now();
        //更新已经进行中的状态
        lambdaUpdate()
                .set(Activity::getStatus,ActivityStatusEnum.DISTRIBUTING.getStatus())
                .eq(Activity::getStatus, NO_DISTRIBUTE.getStatus())
                .le(Activity::getDistributeStartTime, now)
                .gt(Activity::getDistributeEndTime, now)
                .update();
        //更新已经结束的
        lambdaUpdate()
                .set(Activity::getStatus, LOSE_EFFICACY.getStatus())
                .in(Activity::getStatus, Arrays.asList(DISTRIBUTING.getStatus(), NO_DISTRIBUTE.getStatus()))
                .lt(Activity::getDistributeEndTime, now)
                .update();
    }

    @Override
    public void preHeat(){
        //查询活动列表(30天内进行中的和待开始的，结果根据活动开始时间倒序)
        List<Activity> activityList = this.lambdaQuery()
                .in(Activity::getStatus, NO_DISTRIBUTE.getStatus(), DISTRIBUTING.getStatus())
                .lt(Activity::getDistributeStartTime, DateUtils.now().plusDays(30))
                .orderByDesc(Activity::getDistributeStartTime)
                .list();
        if (CollUtils.isEmpty(activityList)) {
            activityList = new ArrayList<>();
        }
        //2.将列表数据转为JSON字符串
        List<ActivityInfoResDTO> activityInfoResDTOList = BeanUtils.copyToList(activityList, ActivityInfoResDTO.class);
        String jsonStr = JsonUtils.toJsonStr(activityInfoResDTOList);

        //3.将JSON字符串存入Redis缓存
        String redisKey = RedisConstants.RedisKey.ACTIVITY_CACHE_LIST;
        redisTemplate.opsForValue().set(redisKey,jsonStr);
    }

    @Override
    public List<ActivityInfoResDTO> queryActivityList(Integer tabType) {
        //1.从缓存中查询全部活动列表的JSON字符串
        String redisKey = RedisConstants.RedisKey.ACTIVITY_CACHE_LIST;
        Object jsonStr = redisTemplate.opsForValue().get(redisKey);

        //2.将JSON字符串转为活动列表
        List<ActivityInfoResDTO> activityInfoResDTOList = JsonUtils.toList(jsonStr.toString(), ActivityInfoResDTO.class);

        //3.将用户选中的类型翻译成想要的活动状态
        Integer selectStatus = ObjectUtils.equals(tabType, TabTypeConstants.SEIZING) ? DISTRIBUTING.getStatus() : NO_DISTRIBUTE.getStatus();

        //4.获取每个活动此刻真实的状态
        List<ActivityInfoResDTO> acitivityList = activityInfoResDTOList.stream()
                //筛选出用户想要的状态的活动列表
                .filter(x -> ObjectUtils.equals(selectStatus, getCurrentRealStatus(x, x.getDistributeStartTime(), x.getDistributeEndTime())))
                .peek(x->{
                    x.setRemainNum(x.getStockNum()); //优惠券活动剩余数量等于库存数量
                }).collect(Collectors.toList());

        //5.响应活动列表
        return acitivityList;
    }

    /**
     * 获取每个活动此刻的真实状态
     * @param distributeStartTime
     * @param distributeEndTime
     * @return
     */
    private Integer getCurrentRealStatus(ActivityInfoResDTO dto, LocalDateTime distributeStartTime, LocalDateTime distributeEndTime) {
        //1. 未开始的活动 - 如果活动时间已到，处理成进行中
        if(ObjectUtils.equals(dto.getStatus(),NO_DISTRIBUTE.getStatus()) &&
                DateUtils.now().isAfter(distributeStartTime) && DateUtils.now().isBefore(distributeEndTime)) {
            dto.setStatus(DISTRIBUTING.getStatus());
            return DISTRIBUTING.getStatus();
        } else if(ObjectUtils.equals(dto.getStatus(),NO_DISTRIBUTE.getStatus()) && DateUtils.now().isAfter(distributeEndTime)) {
            //2. 未开始的活动 - 如果活动时间已过，处理成已过期
            dto.setStatus(LOSE_EFFICACY.getStatus());
            return LOSE_EFFICACY.getStatus();
        } else if(ObjectUtils.equals(dto.getStatus(),DISTRIBUTING.getStatus()) && DateUtils.now().isAfter(distributeEndTime)) {
            //3. 进行中的活动 - 如果活动时间已过，处理成已过期
            dto.setStatus(LOSE_EFFICACY.getStatus());
            return LOSE_EFFICACY.getStatus();
        }
        return dto.getStatus();
    }
}
